/****************************************************************************\
*                                                                            *
*  Copyright 2008 The CAnt2 Project (http://code.google.com/p/cant2)         *
*                                                                            *
*  Licensed under the Apache License, Version 2.0 (the "License");           *
*  you may not use this file except in compliance with the License.          *
*  You may obtain a copy of the License at                                   *
*                                                                            *
*      http://www.apache.org/licenses/LICENSE-2.0                            *
*                                                                            *
*  Unless required by applicable law or agreed to in writing, software       *
*  distributed under the License is distributed on an "AS IS" BASIS,         *
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
*  See the License for the specific language governing permissions and       *
*  limitations under the License.                                            *
*                                                                            *
\****************************************************************************/

#include <UnitTest++/src/UnitTest++.h>
#include "CAnt2/src/core/XmlCharacterClassification.h"

#include <vector>
#include <limits>
#include <iostream>
#include <cstddef>

/****************************************************************************/

namespace {

#if 0 // Useful for checking if a change is an optimization or not, but not really a unit test.
    TEST ( ClassificationCodeTiming )
    {
        const unsigned Iterations = 1000000;
        UnitTest::Timer timer;
        int cnt = 0;
        std::vector<char> dummy(1024);

        for ( unsigned i = 0; i < 1024; ++i )
        {
            dummy[i] = char ( std::rand() & 127 );
        }

        timer.Start();
        for ( unsigned i = 0; i < Iterations; ++i )
        {
            cnt += Xml1p1::IsNameContinue ( int ( dummy[ i & 1023 ] ) ) ? 1 : 0;
        }
        int codeTime = timer.GetTimeInMs();
        CHECK ( codeTime > 4 );

        std::cout << "codeTime is " << codeTime << "ms \n";
    }
#endif

    TEST ( TestIsCharacter )
    {
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( 0        ) );
        CHECK_EQUAL ( 1, Xml1p1::IsCharacter   ( 1        ) );
        CHECK_EQUAL ( 1, Xml1p1::IsCharacter   ( 0x00d7ff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( 0x00d7ff + 1 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( 0x00e000 - 1 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsCharacter   ( 0x00e000 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsCharacter   ( 0x00fffd ) );
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( 0x00fffe ) );
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( 0x00ffff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsCharacter   ( 0x010000 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsCharacter   ( 0x10ffff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( 0x10ffff + 1 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsCharacter   ( -1 ) );
    }

    TEST ( TestIsWhitespace )
    {
        CHECK_EQUAL ( 1, Xml1p1::IsWhitespace  ( 0x000020 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsWhitespace  ( 0x000009 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsWhitespace  ( 0x00000d ) );
        CHECK_EQUAL ( 1, Xml1p1::IsWhitespace  ( 0x00000a ) );

        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x000000 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x000001 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x000007 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x000008 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x00000b ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x00000c ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x00000e ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x00000f ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x00001f ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x000021 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace  ( 0x000041 ) );

        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace ( 0  ) );
        CHECK_EQUAL ( 0, Xml1p1::IsWhitespace ( -1 ) );
    }

    TEST ( TestIsNameStart )
    {
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x39 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x3a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x3b ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x40 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x41 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x42 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x59 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x5a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x5b ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x5e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x5f ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x60 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x61 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x62 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x79 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x7a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x7b ) );

        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x0000bf ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0000c0 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0000d6 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x0000d7 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0000d8 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0000f6 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x0000f7 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0000f8 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0002ff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x000300 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00036f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x000370 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00037d ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00037e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00037f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x001fff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x002000 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00200b ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00200c ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00200d ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00200e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x002070 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00218f ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x002190 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x002bff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x002c00 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x002fef ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x002ff0 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x003000 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x003001 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00d7ff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00d800 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00f8ff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00f900 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00fdcf ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00fdd0 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00fdef ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00fdf0 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x00fffd ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00fffe ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x00ffff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x010000 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 0x0effff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0x0f0000 ) );

        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( 0  ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameStart   ( -1 ) );

        CHECK_EQUAL ( 1, Xml1p1::IsNameStart   ( 'x' ) );
    }

    TEST ( TestIsNameContinue )
    {
        // All NameStart character tests
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x39 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x3a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x3b ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x40 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x41 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x42 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x59 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x5a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x5b ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x5e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x5f ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x60 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x61 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x62 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x79 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x7a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x7b ) );

        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x0000bf ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0000c0 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0000d6 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x0000d7 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0000d8 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0000f6 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x0000f7 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0000f8 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0002ff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x000300 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00036f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x000370 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00037d ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00037e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00037f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x001fff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x002000 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00200b ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00200c ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00200d ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00200e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x002070 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00218f ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x002190 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x002bff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x002c00 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x002fef ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x002ff0 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x003000 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x003001 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00d7ff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00d800 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00f8ff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00f900 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00fdcf ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00fdd0 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00fdef ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00fdf0 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00fffd ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00fffe ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00ffff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x010000 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0effff ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x0f0000 ) );

        // NameContinue specific tests
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x2c ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x2d ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x2e ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x2f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x30 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x39 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x3a ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x3b ) );

        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0xb6 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0xb7 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0xb8 ) );

        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x0002ff ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x000300 ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00036f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x000370 ) );

        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x00203e ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x00203f ) );
        CHECK_EQUAL ( 1, Xml1p1::IsNameContinue ( 0x002040 ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0x002041 ) );

        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( 0  ) );
        CHECK_EQUAL ( 0, Xml1p1::IsNameContinue ( -1 ) );
    }

};

